package com.mzplayer.videoview.base;

import android.content.Context;
import android.media.AudioManager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.Surface;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.mzplayer.player.MediaPlayer;
import com.mzplayer.utils.MediaData;
import com.mzplayer.widget.TextureRenderView;

import java.io.IOException;

import tv.danmaku.ijk.media.player.IjkMediaPlayer;

/**
 * @Package: com.mzplayer.videoview.base
 * @ClassName: BaseParent
 * @Description: 基础类
 *               播放器相关逻辑
 * @Author: 麦子
 * @QQ: 3332174009
 * @CreateDate: 2021/6/27 16:46
 */
public abstract class BaseParent extends FrameLayout {

    protected final Context context = getContext();
    protected TextureRenderView trView;
    protected final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

    public static final int STATE_ERROR = -1;
    public static final int STATE_IDLE = 0;
    public static final int STATE_PREPARING = 1;
    public static final int STATE_PREPARED = 2;
    public static final int STATE_PLAYING = 3;
    public static final int STATE_PAUSED = 4;
    public static final int STATE_PLAYBACK_COMPLETED = 5;
    private int playerState = STATE_IDLE;
    private boolean usingMediaCodec;
    private boolean rtspUsingTCP;
    private float speed;
    private MediaPlayer mediaPlayer;
    private MediaData mediaData;
    private final CallBack callBack = new CallBack();
    private int loadingPercent;

    public BaseParent(Context context) {
        this(context, null);
    }

    public BaseParent(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BaseParent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        IjkMediaPlayer.loadLibrariesOnce(null);
    }


    private boolean isInPlaybackState() {
        return (mediaPlayer != null && playerState != STATE_ERROR && playerState != STATE_IDLE && playerState != STATE_PREPARING);
    }

    public int getPlayerState() {
        return playerState;
    }

    public boolean isPlaying() {
        return isInPlaybackState() && mediaPlayer.isPlaying();
    }

    protected void doPauseAndPlay() {
        if (isPlaying()) {
            pause();
        } else {
            start();
        }
    }

    /**
     * 自定义播放器内核可实现player包内的MediaPlayer接口(参考IJKMediaPlayer)后实现本方法
     *
     * @return
     */
    protected abstract MediaPlayer createPlayer();

    public void openVideo(MediaData mediaData) {
        setMediaData(mediaData);
        openVideo();
    }

    public void openVideo() {
        try {
            reset();
            playerState = STATE_PREPARING;
            audioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
            mediaPlayer = createPlayer();
            if(usingMediaCodec) {
                mediaPlayer.usingMediaCodec();
            }
            if(rtspUsingTCP) {
                mediaPlayer.rtspUsingTCP();
            }
            mediaPlayer.setCallback(callBack);
            mediaPlayer.setDataSource(mediaData);
            trView = new TextureRenderView(this);
            addView(trView, 0, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
            mediaPlayer.prepareAsync();
        } catch (IOException e) {
            playerState = STATE_ERROR;
            callBack.onError(-1, -1);
        }
    }

    public void setMediaData(MediaData mediaData) {
        this.mediaData = mediaData;
        callBack.reset();
    }

    public void setUsingMediaCodec(boolean usingMediaCodec) {
        this.usingMediaCodec = usingMediaCodec;
    }

    public void setRtspUsingTCP(boolean rtspUsingTCP) {
        this.rtspUsingTCP = rtspUsingTCP;
    }

    public MediaData getMediaData() {
        return mediaData;
    }

    public void start() {
        if (isInPlaybackState()) {
            mediaPlayer.start();
            playerState = STATE_PLAYING;
            onPlaying();
        }
    }

    protected abstract void onPlaying();

    public void pause() {
        if (isPlaying()) {
            mediaPlayer.pause();
            playerState = STATE_PAUSED;
            onPaused();
        }
    }

    /**
     * stop方法好像没什么用
     * 停止可以用reset方法
     * 其他可以用pause
     */
    public void stop() {
        pause();
        if (getCurrentPosition() > 0) {
            seekTo(0);
        }
    }

    protected abstract void onPaused();

    public void seekTo(long position) {
        if (isInPlaybackState()) {
            mediaPlayer.seekTo(position * 1000);
        }
    }

    /**
     * 这里保存播放速率,否则会有跟ui不协调的问题
     * @param speed
     */
    public void setSpeed(float speed) {
        if (mediaPlayer != null) {
            mediaPlayer.setSpeed(speed);
            this.speed = speed;
        }
    }

    public long getTcpSpeed() {
        if (isInPlaybackState()) {
            return mediaPlayer.getTcpSpeed();
        }
        return 0;
    }

    public int getLoadingPercent() {
        return loadingPercent;
    }

    /**
     * ijkplayer存在非标准视频源播放完毕后位置不准确的问题
     * 一般相差1秒
     * 这里采用小于最大值则四舍五入
     * 其实应该也可以用向上取整的方式 不知道会不会有问题
     * @return
     */
    public long getCurrentPosition() {
        if (isInPlaybackState()) {
            float currentPosition = mediaPlayer.getCurrentPosition() / 1000f;
            return currentPosition < getDuration() ? Math.round(currentPosition) : (long) currentPosition;
        }
        return 0;
    }

    public long getDuration() {
        if (isInPlaybackState()) {
            return mediaPlayer.getDuration() / 1000;
        }
        return 0;
    }

    /**
     * 没必要添加setDisplay方法
     * 如果使用surfaceView的话,可以直接surfaceHolder.getSurface获得surface
     * @param surface
     */
    public void setSurface(Surface surface) {
        if (mediaPlayer != null) {
            mediaPlayer.setSurface(surface);
        }
    }

    /**
     * reset方法耗时 所以放在线程内执行
     * 但可能存在多次创建新线程的问题会耗费资源
     * 可创建线程池重复利用,不知道会不会有阻塞的问题
     */
    public void reset() {
        pause();
        if (mediaPlayer != null) {
            final MediaPlayer mediaPlayer_r = mediaPlayer;
            mediaPlayer = null;
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    mediaPlayer_r.reset();
                    mediaPlayer_r.release();
                }
            }.start();
            if (trView != null) {
                removeView(trView);
            }
        }
        audioManager.abandonAudioFocus(null);
        playerState = STATE_IDLE;
    }

    protected abstract void onLoadingStart();

    protected abstract void onLoadingEnd();

    protected abstract void onPreparing();

    protected abstract void onPrepared();

    protected abstract void onCompletion();

    protected abstract void onError(int what, int extra);

    protected abstract void onBufferingUpdate(int position);

    protected abstract void onVideoSizeChanged(int width, int height, int sar_num, int sar_den);

    protected abstract void onTimedText(String text);

    class CallBack implements MediaPlayer.Callback {
        private long recordPosition;

        public void reset() {
            recordPosition = 0;
        }

        @Override
        public void onLoadingStart() {
            loadingPercent = 0;
            BaseParent.this.onLoadingStart();
        }

        @Override
        public void onLoadingFlush(int percent) {
            loadingPercent = percent;
        }

        @Override
        public void onLoadingEnd() {
            loadingPercent = 100;
            BaseParent.this.onLoadingEnd();
        }

        @Override
        public void onVideoRotationChanged(int degree) {
            if (trView != null) {
                trView.setVideoRotation(degree);
            }
        }

        @Override
        public void onVideoSizeChanged(int width, int height, int sar_num, int sar_den) {
            if (trView != null) {
                trView.setVideoSize(width, height);
            }
            BaseParent.this.onVideoSizeChanged(width, height, sar_num, sar_den);
        }

        @Override
        public void onBufferingUpdate(int percent) {
            long duration = getDuration();
            BaseParent.this.onBufferingUpdate(duration > 0 ? (int) (percent >= 95 ? duration : duration < 60 && percent >= 90 ? duration : (long) (percent / 100f * duration)) : 0);
        }

        @Override
        public void onPreparing() {
            if (recordPosition == 0) {
                BaseParent.this.onPreparing();
            }
        }

        @Override
        public void onPrepared() {
            playerState = STATE_PREPARED;
            if (recordPosition != 0) {
                if (getDuration() != 0) {
                    seekTo(recordPosition);
                }
                reset();
                start();
                onLoadingEnd();
            } else {
                BaseParent.this.onPrepared();
            }
            if (speed != 0) {
                setSpeed(speed);
            }
        }

        @Override
        public void onCompletion() {
            playerState = STATE_PLAYBACK_COMPLETED;
            onPaused();
            BaseParent.this.onCompletion();
        }

        /**
         * ijk内核切换网络后会报-10000错误
         * 如果用ijkhttphook不能兼容所有链接
         * 所以默认每次出错后重试一次
         * 重试成功直接seek到出错之前的位置
         * 连续两次错误则回调onError
         * @param what
         * @param extra
         * @return
         */
        @Override
        public boolean onError(int what, int extra) {
            playerState = STATE_ERROR;
            if (recordPosition == 0) {
                recordPosition = mediaPlayer.getCurrentPosition() / 1000;
                if (recordPosition != 0) {
                    onLoadingStart();
                    openVideo();
                    return true;
                }
            } else {
                reset();
                onLoadingEnd();
            }
            onPaused();
            BaseParent.this.onError(what, extra);
            return true;
        }

        @Override
        public void onTimedText(String text) {
            BaseParent.this.onTimedText(text);
        }
    }
}
